/*
 * @brief
 * @auther aolei1024@gmail.com
 * @date 2020-06-10
 */
#ifndef _NAMEOF_HPP_
#define _NAMEOF_HPP_
#include <type_traits>
#include <array>
#include "integer_sequence.hpp"

namespace detail
{


    template <typename E>
    struct enum_range 
    {
        static_assert(std::is_enum<E>::value, "enum_range requires enum type.");
        static constexpr int min = NAMEOF_ENUM_RANGE_MIN;
        static constexpr int max = NAMEOF_ENUM_RANGE_MAX;
        static_assert(max > min, "nameof::enum_range requires max > min.");
    };

    template <typename E>
    constexpr int reflected_min() noexcept 
    {
        static_assert(std::is_enum<E>::value, "nameof::detail::reflected_min requires enum type.");
        constexpr auto lhs = enum_range<E>::min;
        static_assert(lhs > (std::numeric_limits<std::int16_t>::min)(), "nameof::enum_range requires min must be greater than INT16_MIN.");
        constexpr auto rhs = (std::numeric_limits<std::underlying_type_t<E>>::min)();

        return cmp_less(lhs, rhs) ? rhs : lhs;
    }

    template <typename E>
    constexpr int reflected_max() noexcept 
    {
        static_assert(is_enum_v<E>, "nameof::detail::reflected_max requires enum type.");
        constexpr auto lhs = enum_range<E>::max;
        static_assert(lhs < (std::numeric_limits<std::int16_t>::max)(), "nameof::enum_range requires max must be less than INT16_MAX.");
        constexpr auto rhs = (std::numeric_limits<std::underlying_type_t<E>>::max)();

        return cmp_less(lhs, rhs) ? lhs : rhs;
    }

    template <typename E>
    inline constexpr int reflected_min_v = reflected_min<E>();

    template <typename E>
    inline constexpr int reflected_max_v = reflected_max<E>();


    template <typename E, int... I>
    constexpr auto values(std14::integer_sequence<int, I...>) noexcept 
    {
        constexpr std::array<bool, sizeof...(I)> valid{{(n<E, static_cast<E>(I + reflected_min_v<E>)>().size() != 0)...}};
        constexpr int count = ((valid[I] ? 1 : 0) + ...);

        std::array<E, count> values{};
        for (int i = 0, v = 0; v < count; ++i) {
            if (valid[i]) {
                values[v++] = static_cast<E>(i + reflected_min_v<E>);
            }
        }
        return values;
    }
    template <typename E>
    constexpr auto values_v = values<E>(std14::make_integer_sequence<int, range_size<E, reflected_min_v<E>, reflected_max_v<E>>()>{});

    template <typename E>
    constexpr std::size_t count_v = values_v<E>.size();

    template <typename E>
    constexpr int min_v = values_v<E>.empty() ? 0 : static_cast<int>(values_v<E>.front());
}
namespace Temp
{
    template <typename E>
    constexpr auto nameof_enum(E value) noexcept -> detail::enable_if_enum_t<E, std::string_view> 
    {
        using D = typename std::decay<E>::type;
        using U = typename std::underlying_type<D>::type;

        const auto i = static_cast<int>(value) - detail::min_v<D>; 
        if(static_cast<U>(value) >= static_cast<U>(detail::min_v<D>) &&
            static_cast<U>(value) <= static_cast<U>(detail::max_v<D>)) 
        {
            if constexpr (detail::sparsity_v<D>) 
            {
                if (const auto idx = detail::indexes_v<D>[i]; idx != detail::invalid_index_v<D>) 
                {
                    return detail::strings_v<D>[idx];
                }
            } else {
                return detail::strings_v<D>[i];
            }
        }

        return {}; // Value out of range.
    }
}

#endif //_NAMEOF_H_
